package smcms.gateway.filter;

import com.alibaba.fastjson.JSONObject;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.MalformedJwtException;
import io.jsonwebtoken.SignatureException;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import smcms.commons.api.entity.result.CommonsResult;
import smcms.commons.api.entity.util.JwtUtil;

import java.util.ArrayList;
import java.util.List;

@Order(-1)
@Component
public class CustomerFilter implements GlobalFilter {
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private CommonsResult commonsResult;
    @Autowired
    private JwtUtil jwtUtil;
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();
        ArrayList<String> strings = new ArrayList<>();
        strings.add("/login");
        strings.add("/logout");
        strings.add("/auth/register");
        strings.add("/ResourcesController/getCurrentPermission");
        if (StringMatches(path,strings)){
            return chain.filter(exchange);
        }
        HttpHeaders headers = request.getHeaders();
        String token = headers.getFirst("token");
        if (StringUtils.isEmpty(token)) {
            return tokenNotExist(exchange);
        }
        try {
            Claims webToken = jwtUtil.parseToken(token);
            String redisToken = (String) redisTemplate.opsForValue().get(webToken.get("username"));
            if (ObjectUtils.isEmpty(redisToken)) {
                return tokenNotExist(exchange);
            }
        } catch (ExpiredJwtException e){
            return tokenNotExist(exchange);
        } catch (final MalformedJwtException e){
            exchange.getResponse().getHeaders().set(HttpHeaders.CONTENT_TYPE,"application/json");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            CommonsResult res = commonsResult.failed(e.getMessage());
            DataBuffer wrap = exchange.getResponse().bufferFactory().wrap(JSONObject.toJSONString(res).getBytes());
            return exchange.getResponse().writeWith(Mono.just(wrap));
        } catch (final SignatureException e) {
            exchange.getResponse().getHeaders().set(HttpHeaders.CONTENT_TYPE,"application/json");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            CommonsResult res = commonsResult.failed("签名无效");
            DataBuffer wrap = exchange.getResponse().bufferFactory().wrap(JSONObject.toJSONString(res).getBytes());
            return exchange.getResponse().writeWith(Mono.just(wrap));
        }

        return chain.filter(exchange);
    }

    private Mono<Void> tokenNotExist(ServerWebExchange exchange) {
        exchange.getResponse().getHeaders().set(HttpHeaders.CONTENT_TYPE,"application/json");
        exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
        CommonsResult res = commonsResult.failed("用户未认证或token已过期");
        DataBuffer wrap = exchange.getResponse().bufferFactory().wrap(JSONObject.toJSONString(res).getBytes());
        return exchange.getResponse().writeWith(Mono.just(wrap));
    }

    public static Boolean StringMatches(String str, List<String> strs) {
        if (StringUtils.isEmpty(str)||strs.isEmpty()) {
            return false;
        }
        for (String pattern : strs) {
            if (pattern.equals(str)) {
                return true;
            }
        }
        return false;
    }
}
